package com.hyperdenton.HikCameraWrapper;

import ch.qos.logback.core.encoder.EchoEncoder;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

import java.util.Map;
import java.util.TreeMap;

class Dvr {

    private final String dvrIp;
    private final String dvrPort;
    private final String dvrUsername;
    private final String dvrPassword;

    public String getDvrIp() {
        return dvrIp;
    }
    public String getDvrPort() {
        return dvrPort;
    }
    public String getDvrUsername() {
        return dvrUsername;
    }
    public String getDvrPassword() {
        return dvrPassword;
    }

    public Dvr(String dvrIp, String dvrPort, String dvrUsername, String dvrPassword) {
        this.dvrIp = dvrIp;
        this.dvrPort = dvrPort;
        this.dvrUsername = dvrUsername;
        this.dvrPassword = dvrPassword;
    }
}

public class HikCameraSdkWrapper {

    private static final HCNetSDK hcNetSDK = HCNetSDK.INSTANCE;
    private int loginErrorNumber = -1;
    private HCNetSDK.NET_DVR_DEVICEINFO_V30 m_strDeviceInfo; //设备信息
    private NativeLong lUserID;//用户句柄
    private final NativeLong lRealHandle;
    private final Map<String, Integer> commands;
    private final String dvrIp;
    private final String dvrUsername;
    private final String dvrPassword;
    private boolean isLogin = false;

    public HikCameraSdkWrapper(String dvrIp, String dvrPort, String dvrUsername, String dvrPassword) throws Exception {
        Dvr dvr = new Dvr(dvrIp,dvrPort,dvrUsername,dvrPassword);
        this.dvrIp = dvrIp;
        this.dvrUsername = dvrUsername;
        this.dvrPassword = dvrPassword;

        this.login(dvr);

        //用户信息
        HCNetSDK.NET_DVR_CLIENTINFO m_strClientInfo = new HCNetSDK.NET_DVR_CLIENTINFO();
        m_strClientInfo.lChannel = new NativeLong(this.getChannelNumber());

        this.lRealHandle = hcNetSDK.NET_DVR_RealPlay_V30(lUserID,
                m_strClientInfo, null, null, true);

        this.commands = new TreeMap<>();
        this.commands.put("UP",  HCNetSDK.TILT_UP);
        this.commands.put("DOWN",  HCNetSDK.TILT_DOWN);
        this.commands.put("LEFT",  HCNetSDK.PAN_LEFT);
        this.commands.put("RIGHT",  HCNetSDK.PAN_RIGHT);
        this.commands.put("UP_LEFT",  HCNetSDK.UP_LEFT);
        this.commands.put("UP_RIGHT",  HCNetSDK.UP_RIGHT);
        this.commands.put("DOWN_LEFT",  HCNetSDK.DOWN_LEFT);
        this.commands.put("DOWN_RIGHT",  HCNetSDK.DOWN_RIGHT);
        this.commands.put("AUTO",  HCNetSDK.PAN_AUTO);
    }

    public void close(){
        hcNetSDK.NET_DVR_Logout_V30(lUserID);
        hcNetSDK.NET_DVR_Cleanup();
        isLogin = false;
    }

    private void login(Dvr dvr) throws Exception {
        if(isLogin)
            throw new Exception("already logged in!");
        boolean initSuc = hcNetSDK.NET_DVR_Init();
        if (!initSuc){
            throw new Exception("初始化失败");
        }
        //已登录设备的IP地址
        String m_sDeviceIP = dvr.getDvrIp();//设备ip地址
        m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();
        int iPort = Integer.parseInt(dvr.getDvrPort()) ;
        lUserID = hcNetSDK.NET_DVR_Login_V30(m_sDeviceIP,
                (short) iPort, dvr.getDvrUsername(), dvr.getDvrPassword(), m_strDeviceInfo);
        loginErrorNumber = hcNetSDK.NET_DVR_GetLastError();
        long userID = lUserID.longValue();
        if (loginErrorNumber != 0) {
            throw new Exception("注册失败, 错误码：" + loginErrorNumber);
        } else {
            System.out.println("注册成功，userID: " + userID);
        }
        isLogin = true;
    }

    /**
     * 获取设备通道
     */
    private int getChannelNumber() {
        IntByReference ibrBytesReturned = new IntByReference(0);//获取IP接入配置参数
        boolean bRet;
        int iChannelNum = -1;
        //IP参数
        HCNetSDK.NET_DVR_IPPARACFG m_strIpparaCfg = new HCNetSDK.NET_DVR_IPPARACFG();
        m_strIpparaCfg.write();
        Pointer lpIpParaConfig = m_strIpparaCfg.getPointer();
        bRet = hcNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_IPPARACFG,
                new NativeLong(0), lpIpParaConfig, m_strIpparaCfg.size(), ibrBytesReturned);
        m_strIpparaCfg.read();
        String devices = "";
        if (!bRet) {
            //设备不支持,则表示没有IP通道
            for (int iChannum = 0; iChannum < m_strDeviceInfo.byChanNum; iChannum++) {
                devices = "Camera" + (iChannum + m_strDeviceInfo.byStartChan);
            }
        } else {
            for (int iChannum = 0; iChannum < HCNetSDK.MAX_IP_CHANNEL; iChannum++) {
                if (m_strIpparaCfg.struIPChanInfo[iChannum].byEnable == 1) {
                    devices = "IPCamera" + (iChannum + m_strDeviceInfo.byStartChan);
                }
            }
        }
        if (devices.length() != 0) {
            if (devices.charAt(0) == 'C') {//Camara开头表示模拟通道
                //子字符串中获取通道号
                iChannelNum = Integer.parseInt(devices.substring(6));
            } else {
                if (devices.charAt(0) == 'I') {//IPCamara开头表示IP通道
                    //子字符创中获取通道号,IP通道号要加32
                    iChannelNum = Integer.parseInt(devices.substring(8)) + 32;
                } else {
                    return 4;
                }
            }
        }
        return iChannelNum;
    }

    public String getRtspUrl() throws Exception {
        int channel = getChannelNumber();
        if(loginErrorNumber != 0){
            throw new Exception("login first please");
        }

        if(channel == -1){
            throw new Exception("获取通道号失败");
        }
        return "rtsp://"+ dvrUsername + ":" + dvrPassword + "@" + dvrIp
                + "/h264" + "/ch" + getChannelNumber() + "/main/av_stream";
    }

    /*************************************************
     函数名:          PTZControlAll
     函数描述:        云台控制函数
     输入参数:
     lRealHandle:   预览句柄
     iPTZCommand:   PTZ控制命令
     iStop:         开始或是停止操作
     输出参数:
     返回值:
     *************************************************/
    private void PTZControlAll(int iSpeed, int iPTZCommand, int iStop) throws Exception {
        boolean ret;
        ret = hcNetSDK.NET_DVR_PTZControlWithSpeed(lRealHandle, iPTZCommand, iStop, iSpeed);
        if (!ret) {
            throw new Exception("云台控制失败");
        }
    }

    public void control(String direction, int speed, int duration) throws Exception {
    /*
         TILT_UP 21	     云台以SS的速度上仰
         TILT_DOWN 22	 云台以SS的速度下俯
         PAN_LEFT 23	 云台以SS的速度左转
         PAN_RIGHT 24	 云台以SS的速度右转
         UP_LEFT 25	     云台以SS的速度上仰和左转
         UP_RIGHT 26	 云台以SS的速度上仰和右转
         DOWN_LEFT 27	 云台以SS的速度下俯和左转
         DOWN_RIGHT 28	 云台以SS的速度下俯和右转
         PAN_AUTO 29	 云台以SS的速度左右自动扫描
    */
        int iPTZCommand;
        try {
            iPTZCommand = commands.get(direction);
        }catch (NullPointerException e){
            System.err.println("Command \""+direction+"\" not found!");
            throw e;
        }
        if(iPTZCommand != -1){
            synchronized (this) {
                PTZControlAll(speed, iPTZCommand, 0);
                Thread.sleep(duration);
                PTZControlAll(speed, iPTZCommand, 1);
            }
        }
    }
}